home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1998 July / EnigmA AMIGA RUN 29 (1998)(G.R. Edizioni)(IT)[!][issue 1998-07 & 08].iso / recent / mergea.lha / MergeAIFF / MergeAIFF.cpp < prev    next >
C/C++ Source or Header  |  1998-01-09  |  14KB  |  399 lines

  1. /*          $VER: MergeAIFF 1.0 (9.1.98)
  2. **       Started: 980102
  3. **      Location: Waratah.au
  4. **    Last drunk: 3 days ago
  5. **   Last stoned: 12 days ago (H)
  6. **          Mood: Homesick
  7. ** Todays wishes: 1> Score 2> Get stoned 3> Return to Sweden
  8. **       Changed: 980105, 980106, 980107, 980108
  9. ** 
  10. **  ::::::.::::::. :::::::.   .::  ::::::. .::::.  :::
  11. **  .::.::'.::..:: .::..::' ...`::..::..'' .::.    `' 
  12. **  :::    :::: ::::::: `::.::::::'::::.::.::::::' :::  Captain's log...
  13. **
  14. ** 980102:2255 (001) Made some short routines to open the specified files and check if the
  15. **             first bytes are 'FORM'.
  16. ** 980105:1908 (001) Scrapped above mentioned routines. Of course I am to use IffParse!
  17. **             Studied iff-parse docs. Hmm...
  18. ** 980105:2322 (002) Managed to get iff-parse to read the COMM chunk and put the results
  19. **             in a comm-structure that I copied after the AIFF doc.
  20. ** 980106:0110 (002) Now this is stored in the destination (with size = the sum of the sizes
  21. **             of source a and b), but thereafter I cannot find the SSND chunk in the
  22. **             source file.
  23. ** 980106:0708 (002) Fixed this problem by using StopChunks() instead. Made the first
  24. **             successfull merge! Now let's see if it can merge files larger than the
  25. **             available memory...
  26. ** 980106:0735 (002) No problems! It only seem to use the buffer's memory. I'm trying now
  27. **             to have the buffersize = AvailMem(Largest).
  28. ** 980106:1112 (003) Made a very primitive progress indicator. Using a buffersize of 75% of
  29. **             the largest available memoryblock now. I am going to try to implement several
  30. **             sourcefiles now (more than 2).
  31. ** 980106:1245 (004) Implemented this, but haven't got it to compile yet. Changed the name
  32. **             from MergeAIFF to JoinAIFF. Like the c:Join an 'as' is required before the
  33. **             destination filename.
  34. ** 980107:2355 (005) Everything works ok now. 75% of the largest available memoryblock will
  35. **             now be adjusted after the largest file. But I'm going to remove this, there's
  36. **             no reason to use such a large buffer. Can't remember why I did so in the first
  37. **             place.
  38. ** 980108:1502 (005) Now I'm using a buffersize of 256kb. If the allocation fails, the buffer-
  39. **             size will be halved, and a new try made. This continues until success, or until
  40. **             the buffersize gets too small. Perfected the progress-indicator.
  41. ** 980108:1737 (005) A destination will no longer be overwritten if it already exists.
  42. ** 980109:0040 (005) I am going to upload v1.0 to AmiNet now.
  43. ** 980109:0104 (005) Sigh. There was already a program there called JoinAIFF. Changed the
  44. **             name back again to MergeAIFF. (JoinAIFF only joins 2 mono to one stereo).
  45. */
  46.  
  47. #include <ctype.h>
  48. #include <stdio.h>
  49. #include <string.h>
  50. #include <exec/exec.h>
  51. #include <clib/exec_protos.h>
  52. #include <dos/dos.h>
  53. #include <clib/dos_protos.h>
  54. #include <libraries/iffparse.h>
  55. #include <clib/iffparse_protos.h>
  56.  
  57. #define MAXSRC 20
  58. #define    ID_AIFF    MAKE_ID('A','I','F','F')
  59. #define    ID_COMM    MAKE_ID('C','O','M','M')
  60. #define    ID_SSND    MAKE_ID('S','S','N','D')
  61.  
  62. const char versiontag[] = "\0$VER: MergeAIFF 1.0 (9.1.98)";
  63. const char *ja_Banner = "\e[33;1mMergeAIFF 1.0\e[31;0m - By \e[1mParsec\e[0m/Phuture303 1998\n";
  64. const char *ja_Template = "Usage: MergeAIFF <source 1> <source 2> [<source 3..20>] AS <output>\n";
  65. const char *Err_IFFHds = "Couldn't allocate IFF handlers.\n";
  66. const char *Err_NoCOMM = "does not seem to contain a COMM chunk.\n";
  67. const char *Err_NoSSND = "does not seem to contain a SSND chunk.\n";
  68. const char *Err_Error = "Error!\n";
  69.  
  70. /* Text error messages for possible IFFERR_#? returns from various
  71. ** IFF routines.  To get the index into this array, take your IFFERR code,
  72. ** negate it, and subtract one. idx = -error - 1 */
  73. const char *ErrorMsgs[] = {
  74.       "End of file (not an error).\n",
  75.       "End of context (not an error).\n",
  76.       "No lexical scope.\n",
  77.       "Insufficient memory.\n",
  78.       "Stream read error.\n",
  79.       "Stream write error.\n",
  80.       "Stream seek error.\n",
  81.       "File is corrupt.\n",
  82.       "IFF syntax error.\n",
  83.       "Not an IFF file.\n",
  84.       "Required call-back hook missing.\n",
  85.       "Return to client.  You should never see this.\n" };
  86.  
  87. struct CommonChunk
  88.        {
  89.        UWORD  numChannels;
  90.        ULONG  numSampleFrames;
  91.        WORD   sampleSize;
  92.        BYTE   sampleRate[10];
  93.        }      comms[MAXSRC], commd;
  94.  
  95. struct SoundDataChunk
  96.        {
  97.        ULONG  offset;
  98.        ULONG  blockSize;
  99.        }      ssnds[MAXSRC], ssndd;
  100.  
  101.  
  102. void ja_CopySSND(struct IFFHandle *iff, struct CommonChunk *comm, const char *filename);
  103. void ja_CleanUp(void);
  104. void ja_Exit(const char *ErrorSpec, const char *ErrorMsg, const int ErrorCode);
  105. void ja_Exit(const char *ErrorMsg, const int ErrorCode);
  106. void ja_Exit(const int ErrorCode);
  107. void ja_FreeIFF(struct IFFHandle *&iff);
  108. void CheckBreak(void);
  109.  
  110. struct IFFHandle *iffs[MAXSRC], *iffd = NULL;
  111. struct ContextNode *cns[MAXSRC], *cnd;
  112.  
  113. APTR buffer;
  114. ULONG buffsize = 256000, progr = 0;
  115. LONG error = 0, rlen = 0;
  116. int sources;
  117. UWORD *ja_SamplePtr;
  118.  
  119. int main(int argc, char **argv)
  120.     {
  121.     LONG stopchks[] = {ID_AIFF, ID_COMM, ID_AIFF, ID_SSND};
  122.     int n;
  123.     sources = argc - 3;
  124.  
  125.     printf("%s", ja_Banner);
  126.     for (n = 0; n < MAXSRC; n++) {iffs[n] = NULL;}
  127.     if (argc < 4 || argc > MAXSRC+3 || !argv[argc-2][0])
  128.        {
  129.        ja_Exit(ja_Template, 10);
  130.        }
  131.     argv[argc-2][0] = tolower(argv[argc-2][0]);
  132.     argv[argc-2][1] = tolower(argv[argc-2][1]);
  133.     if (strcmp(argv[argc-2], "as"))
  134.        {
  135.        ja_Exit(ja_Template, 10);
  136.        }
  137.  
  138.     for (n=0; n<sources; n++)       /* Open all given files and allocate an */
  139.         {                               /* IFF-structure for them. */
  140.         if (!(iffs[n] = AllocIFF()))
  141.            {
  142.            ja_Exit(Err_IFFHds, 20);
  143.            }
  144.         if (!(iffs[n]->iff_Stream = Open(argv[n+1], MODE_OLDFILE)))
  145.            {
  146.            ja_Exit(argv[n+1], "could not be opened for input.\n", 20);
  147.            }
  148.         } // for
  149.  
  150.     while (!buffer && buffsize > 20000)      /* Allocate the copy buffer */
  151.           {
  152.           if (!(buffer = AllocMem(buffsize, 0)))
  153.              {
  154.              buffsize /= 2;
  155.              }
  156.           } // while
  157.     if (!buffer)
  158.        {
  159.        ja_Exit("Couldn't allocate the minimium buffersize of 20kb.\n", 10);
  160.        }
  161.      printf("Using a buffersize of %d bytes.\n", buffsize);
  162.  
  163.  
  164.     if (!(iffd = AllocIFF()))                  /* Open the output file */
  165.        {
  166.        ja_Exit(Err_IFFHds, 20);
  167.        }
  168.     if (iffd->iff_Stream = Open(argv[argc-1], MODE_OLDFILE))
  169.        {
  170.        ja_Exit(argv[argc-1], "already exists!\n", 20);
  171.        }
  172.     if (!(iffd->iff_Stream = Open(argv[argc-1], MODE_NEWFILE)))
  173.        {
  174.        ja_Exit(argv[argc-1], "could not be opened for output.\n", 20);
  175.        }
  176.  
  177.     for (n=0; n<sources; n++)           /* Find the sourcefiles' COMM chunks */
  178.         {
  179.         InitIFFasDOS(iffs[n]);
  180.         if (error = OpenIFF (iffs[n], IFFF_READ))
  181.            {
  182.            ja_Exit(argv[n+1], "failed on OpenIFF for read.\n", 20);
  183.            }
  184.         if (error = StopChunks(iffs[n], stopchks, 2))   // Stop at COMM or SSND chunks
  185.            {
  186.            ja_Exit(argv[n+1], "failed on StopChunks.\n", 20);
  187.            }
  188.         if (error = ParseIFF(iffs[n], IFFPARSE_SCAN))      // Find the COMM chunk...
  189.            {
  190.            ja_Exit(argv[n+1], Err_NoCOMM, 20);
  191.            }
  192.         cns[n] = CurrentChunk(iffs[n]);
  193.         if ((cns[n]) && (cns[n]->cn_Type == ID_AIFF) && (cns[n]->cn_ID == ID_COMM))
  194.            {
  195.            if ((rlen = ReadChunkBytes(iffs[n], &comms[n], sizeof(CommonChunk))) <= 0)
  196.               {
  197.               error = rlen;
  198.               ja_Exit(argv[n+1], "seem to have a corrupted COMM chunk.\n", 20);
  199.               } // if
  200.            } // if
  201.         else
  202.            {
  203.            ja_Exit(argv[n+1], Err_NoCOMM, 20);
  204.            }
  205.         } // for
  206.  
  207.  
  208.     for (n=1; n<sources; n++)              /* Check that the samples geometries match */
  209.         {
  210.         if (comms[n].numChannels != comms[0].numChannels)
  211.            {
  212.            ja_Exit(argv[n+1], "does not have the same number of channels as the first source.\n", 10);
  213.            }
  214.         if (comms[n].sampleSize != comms[0].sampleSize)
  215.            {
  216.            ja_Exit(argv[n+1], "Is not sampled with the same amount of bits as the first source.\n", 10);
  217.            }
  218.         } // for
  219.  
  220.  
  221.     InitIFFasDOS(iffd);                           /* Store the FORM and COMM chunks */
  222.     if (error = OpenIFF (iffd, IFFF_WRITE))
  223.        {
  224.        ja_Exit("OpenIFF for write on destination failed.\n", 20);
  225.        }
  226.     if (!(error=PushChunk(iffd, ID_AIFF, ID_FORM, IFFSIZE_UNKNOWN)))
  227.        {
  228.        if (!(error=PushChunk(iffd, 0, ID_COMM, IFFSIZE_UNKNOWN)))
  229.           {
  230.           commd = comms[0];                                // Source a's COMM chunk => dest
  231.           for (n=1; n<sources; n++)
  232.               {
  233.               commd.numSampleFrames += comms[n].numSampleFrames;  // Add sampleframe sizes
  234.               }
  235.           if (WriteChunkBytes(iffd, &commd, sizeof(CommonChunk)) != sizeof(CommonChunk))
  236.              {
  237.              error = IFFERR_WRITE;
  238.              }
  239.           } // if
  240.        if (!error)
  241.           {
  242.           error = PopChunk(iffd);
  243.           }
  244.        } // if
  245.     if (error)
  246.        {
  247.        ja_Exit("Error writing COMM data to destination.\n", 20);
  248.        }
  249.  
  250.     
  251.     for (n=0; n<sources; n++)                /* Find the SSND chunk in all sources */
  252.         {
  253.         if (error = StopChunks(iffs[n], stopchks, 2))   // Stop at COMM or SSND chunks
  254.            {
  255.            ja_Exit(argv[n+1], "failed on StopChunks.\n", 20);
  256.            }
  257.         if (error = ParseIFF(iffs[n], IFFPARSE_SCAN))       // Find the SSND chunk...
  258.            {
  259.            ja_Exit(argv[n+1], Err_NoSSND, 20);
  260.            }
  261.         cns[n] = CurrentChunk(iffs[n]);
  262.         if ((cns[n]) && (cns[n]->cn_Type == ID_AIFF) && (cns[n]->cn_ID == ID_SSND))
  263.            {
  264.            if ((rlen = ReadChunkBytes(iffs[n], &ssnds[n], sizeof(SoundDataChunk))) <= 0)
  265.               {
  266.               error = rlen;
  267.               ja_Exit(argv[n+1], "seem to have a corrupted COMM chunk.\n", 20);
  268.               } // if
  269.            if (ssnds[n].offset || ssnds[n].blockSize)  // Blocksize & Offset must not be used
  270.               {
  271.               ja_Exit(argv[n+1], "has blocksize or offset set to non-zero.\n",10);
  272.               }
  273.            }
  274.         else
  275.            {
  276.            ja_Exit(argv[n+1], Err_NoSSND, 20);
  277.            }
  278.         } // for
  279.     ssndd = ssnds[0];
  280.  
  281.     if (!(error=PushChunk(iffd, 0, ID_SSND, IFFSIZE_UNKNOWN)))   /* Store the SSND chunk */
  282.        {
  283.        if (WriteChunkBytes(iffd, &ssndd, sizeof(SoundDataChunk)) != sizeof(SoundDataChunk))
  284.           {
  285.           error = IFFERR_WRITE;
  286.           }
  287.        } // if
  288.     if (error)
  289.        {
  290.        ja_Exit("Error writing SSND data to destination.\n", 20);
  291.        }
  292.     for (n=0; n<sources; n++)
  293.         {
  294.         ja_CopySSND(iffs[n], &comms[n], argv[n+1]);
  295.         }
  296.  
  297.     if (error = PopChunk(iffd))
  298.        {
  299.        ja_Exit(20);
  300.        }
  301.     printf("%d files successfully merged.\n", sources);
  302.     ja_Exit(0);
  303.     } // main
  304.  
  305.  
  306. void ja_CopySSND(struct IFFHandle *iff, struct CommonChunk *comm, const char *filename)
  307.      {
  308.      ULONG size = ((comm->sampleSize + 7) / 8) * comm->numChannels * comm->numSampleFrames;
  309.      UBYTE skipchars = (size<10 ? 1 : size<100 ? 2 : size<1000 ? 3 : size<10000 ? 4 : size<100000 ? 5 : size<1000000 ? 6 : size<10000000 ? 7 : size<100000000 ? 8 : size<1000000000 ? 9 : 10) + strlen(filename) + 9;
  310.  
  311.      rlen = 1;
  312.      progr = 0;
  313.      printf("Reading (%d) %s %%0\r", size, filename);
  314.      while (rlen > 0)
  315.            {
  316.            printf("Read\e[%dC%d\r", skipchars, (100 * progr) / size);
  317.            fflush(stdout);
  318.            CheckBreak();
  319.            if ((rlen = ReadChunkBytes(iff, buffer, buffsize)) > 0)
  320.               {
  321.               printf("Writ\e[%dC%d\r", skipchars, (100 * (progr + rlen/2)) / size);
  322.               fflush(stdout);
  323.               CheckBreak();
  324.               if (WriteChunkBytes(iffd, buffer, rlen) != rlen)
  325.                  {
  326.                  error = IFFERR_WRITE;
  327.                  ja_Exit("\n", Err_Error, 20);
  328.                  } // if
  329.               progr += rlen;
  330.               } // if
  331.            } // while
  332.      if (rlen < 0 && rlen != IFFERR_EOF)
  333.         {
  334.         error = rlen;
  335.         ja_Exit("\n", Err_Error, 20);
  336.         } // if
  337.      printf(" Merged\e[%dC100\n", skipchars-3);
  338.      } // CopySSND
  339.  
  340. void CheckBreak(void)                     /* Check if CTRL-C is pressed */
  341.      {
  342.      if (SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
  343.     {
  344.     ja_Exit("\n***Break\n", 10);
  345.     }
  346.      } // CheckBreak
  347.  
  348. void ja_Exit(const char *ErrorSpec, const char *ErrorMsg, const int ErrorCode)
  349.      {
  350.      printf("%s ", ErrorSpec);
  351.      ja_Exit(ErrorMsg, ErrorCode);
  352.      } // Exit
  353.  
  354. void ja_Exit(const char *ErrorMsg, const int ErrorCode)
  355.      {
  356.      printf("%s", ErrorMsg);
  357.      ja_Exit(ErrorCode);
  358.      } // Exit
  359.  
  360. void ja_Exit(const int ErrorCode)
  361.      {
  362.      if (error)
  363.         {
  364.         printf("IFFParse error: %s", ErrorMsgs[-error-1]);
  365.         }
  366.      ja_CleanUp();
  367.      exit(ErrorCode);
  368.      } // Exit
  369.  
  370. void ja_CleanUp(void)
  371.      {
  372.      ja_FreeIFF(iffd);
  373.      for (int n=0; n<sources; n++)
  374.          {
  375.          ja_FreeIFF(iffs[n]);
  376.          }
  377.      if (buffer)
  378.         {
  379.         FreeMem(buffer, buffsize);
  380.         buffer = NULL;
  381.         } // if
  382.      } // CleanUp
  383.  
  384. void ja_FreeIFF(struct IFFHandle *&iff)  // Takes a reference to an iff-structure
  385.      {
  386.      if (iff)
  387.         {
  388.         CloseIFF(iff);
  389.         if (iff->iff_Stream)
  390.            {
  391.            Close(iff->iff_Stream);
  392.            iff->iff_Stream = NULL;
  393.            }
  394.         FreeIFF(iff);
  395.         iff = NULL;
  396.         } // if
  397.      } // FreeIFF
  398.  
  399.